# -*- coding: UTF-8 -*-

import os
import re
import time
import json
import base64
import shutil
import string
import zipfile
import tarfile
import threading
from collections import OrderedDict


def Singleton(cls):
    _instance = {}

    def wrapper(*args, **kw):
        if cls not in _instance:
            _instance[cls] = cls(*args, **kw)
        return _instance[cls]
    return wrapper


class JsonParser:
    """ JSON解析器 """
    @classmethod
    def load(cls, json_path):
        with open(json_path, 'r', encoding='UTF-8') as f:
            return json.load(f, object_pairs_hook=OrderedDict)

    @classmethod
    def write(cls, json_path, data):
        with open(json_path, 'w', encoding='UTF-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=4)

    @classmethod
    def dumps(cls, data):
        return json.dumps(data, ensure_ascii=False)

    @classmethod
    def loads(cls, data):
        return json.loads(data)


class Common:
    """ 第三方公共方法 """
    @classmethod
    def is_exists(cls, dir):
        return os.path.exists(dir)

    @classmethod
    def is_file(cls, path):
        return os.path.isfile(path)

    @classmethod
    def is_dir(cls, path):
        return os.path.isdir(path)

    @classmethod
    def rm_dir(cls, path):
        try:
            shutil.rmtree(path)
        except:
            pass

    @classmethod
    def mkdir(cls, dir):
        try:
            os.makedirs(dir)
        except FileExistsError:
            pass

    @classmethod
    def remove(cls, path):
        try:
            os.remove(path)
        except:
            pass

    @classmethod
    def rename(cls, old, new):
        try:
            os.rename(old, new)
        except:
            pass

    @classmethod
    def normal_path(cls, path):
        return os.path.normpath(path).replace('\\', '/')

    @classmethod
    def system_open(cls, file):
        try:
            os.startfile(file)
        except:
            pass

    @classmethod
    def get_pid(cls):
        return os.getpid()

    @classmethod
    def basename(cls, path):
        return os.path.split(path)[1]

    @classmethod
    def dirname(cls, path):
        return os.path.split(path)[0]

    @classmethod
    def file_size(cls, file_path):
        if not cls.is_file(file_path):
            return 0
        return os.path.getsize(file_path)

    @classmethod
    def sleep(cls, sec):
        time.sleep(sec)

    @classmethod
    def time(cls):
        return time.time()

    @classmethod
    def get_time(cls, fmt=True):
        if fmt:
            return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        return time.strftime("%Y%m%d%H%M%S", time.localtime())

    @classmethod
    def dos2unix(cls, filepath):
        lines = []
        with open(filepath, 'r', encoding='UTF-8') as f:
            for line in f:
                lines.append(line.replace('\r\n', '\n'))
        cls.write_to_file(filepath, ''.join(lines))

    @classmethod
    def write_to_file(cls, filepath, info):
        with open(filepath, 'w', encoding='UTF-8', newline='\n') as f:
            f.write(info)

    @classmethod
    def write_append_file(cls, filepath, info):
        with open(filepath, 'a+', encoding='UTF-8', newline='\n') as f:
            f.write(info)

    @classmethod
    def create_thread(cls, func, args=()):
        th = threading.Thread(target=func, args=args)
        th.setDaemon(True)
        th.start()
        return th

    @classmethod
    def is_ip(cls, ip):
        p = re.compile("^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$")
        return p.match(ip)

    @classmethod
    def get_windows_desktop_path(cls):
        return cls.normal_path(os.path.join(os.path.expanduser("~"), 'Desktop'))

    @classmethod
    def get_windows_disks(cls):
        return [disk for c in string.ascii_uppercase if os.path.isdir(disk := '%s:/' % c)]

    @classmethod
    def list_dir(cls, path):
        for root, dirs, files in os.walk(path):
            return root, dirs, files

    @classmethod
    def trans_char(cls, name):
        """ 特殊字符转换成下划线 """
        special = r"[ ()\/\\\:\*\?\"\<\>\|]"
        return re.sub(special, "_", name)

    @classmethod
    def zip_dir(cls, path, file_path):
        """ 压缩目前只考虑.tar.gz，zipfile打包中文文件会乱码 """
        with tarfile.open(file_path, "w:gz") as f:
            f.add(path, arcname='')
    
    @classmethod
    def unzip_file(cls, file_path, path):
        """ 解压缩尝试两种方式，与fucnction_utils.sh中Compress对应 """
        if tarfile.is_tarfile(file_path):
            with tarfile.open(file_path, 'r:gz') as f:
                f.extractall(path)
            return True
        if zipfile.is_zipfile(file_path):
            with zipfile.ZipFile(file_path, 'r') as f:
                f.extractall(path)
            return True
        return False

    @classmethod
    def exist_suffix_file(cls, dirname, suffix):
        for file in os.listdir(dirname):
            f_split = os.path.splitext(file)
            if f_split[1] == suffix:
                return True, f_split[0]
        return False, None

    @classmethod
    def base64_encode(cls, data):
        return str(base64.b64encode(data.encode(encoding='utf-8')), encoding='utf-8')


class Timer(threading.Thread):
    """ 定时器 """
    def __init__(self, func, period=10, delay=0, do_first=False, args=()):
        super(Timer, self).__init__()
        self.func = func
        self.do_first = do_first
        self.period = period
        self.delay = delay
        self.args = args
        self.daemon = True
        self._pause_flag = threading.Event()
        self._run_flag = threading.Event()
        self._pause_flag.set()
        self._run_flag.set()

    def run(self):
        _run_flag = self._run_flag.isSet
        _pause_wait = self._pause_flag.wait
        _sleep = time.sleep

        _sleep(self.delay)

        while _run_flag():
            _pause_wait()
            if self.do_first:
                self.func(self.args)
                _sleep(self.period)
            else:
                _sleep(self.period)
                self.func(self.args)

    def pause(self):
        self._pause_flag.clear()

    def resume(self):
        self._pause_flag.set()

    def stop(self):
        self._pause_flag.set()
        self._run_flag.clear()

    def update_period(self, per):
        self.period = per
